"""
    doc: docs/BXH_REST_API_CN.md
"""

import hashlib
import hmac
import time
from ubroker import SpotBroker
from urllib.parse import urlencode


class XTMSpot(SpotBroker):
	intervals = ('1m', '5m', '15m', '30m', '1h', '1d', '1w', '1M')
	status = dict(
		NEW=SpotBroker.Created, PARTIALLY_FILLED=SpotBroker.Partial, FILLED=SpotBroker.Filled,
		PENDING_CANCEL=SpotBroker.Revoking, CANCELED=SpotBroker.Canceled, REJECTED=SpotBroker.Rejected
	)
	
	def __init__(self, **params):
		super().__init__('https://www.thextomo.com', **params)
	
	def sign(self, **kwargs):
		kwargs.update(dict(timestamp=int(time.time() * 1000), recvWindow=5000))
		kwargs = {key: value for key, value in kwargs.items() if value is not None}
		kwargs['signature'] = hmac.new(
			self.params['secret'].encode('utf-8'), urlencode(kwargs).encode('utf-8'), hashlib.sha256
		).hexdigest()
		self.headers.update({'X-BH-APIKEY': self.params['key']})
		return kwargs
	
	def order_format(self, symbol, order):
		return dict(
			id=order['orderId'], symbol=symbol, time=order['time'],
			side=SpotBroker.Buy if order['side'] == 'BUY' else SpotBroker.Sell, price=order['price'],
			quantity=order['origQty'], traded_quantity=order['executedQty'],
			state=self.status[order['status']]
		)
	
	@staticmethod
	def trade_format(symbol, trade):
		return dict(
			id=trade['id'], time=trade['time'], order_id=trade['orderId'], symbol=symbol,
			side=SpotBroker.Buy if trade['isBuyer'] else SpotBroker.Sell, price=trade['price'], quantity=trade['qty'],
			commission=trade['commission'], commission_asset=trade['commissionAsset']
		)
	
	async def http_requests(self, method, path, **kwargs):
		response = await super().http_requests(method, path, **kwargs)
		if isinstance(response, dict) and response.get('code'):
			raise TypeError(response)
		return response
	
	async def basic(self):
		path = '/openapi/v1/brokerInfo'
		response = await self.http_requests(SpotBroker.GET, path)
		return [dict(
			base=item['baseAsset'], quote=item['quoteAsset'], symbol=item['symbol'],
			price_tick=item['filters'][0]['tickSize'], min_quantity=item['filters'][1]['minQty'],
			min_notional=item['filters'][2]['minNotional']
		) for item in response['symbols']]
	
	async def depth(self, symbol):
		path, params = '/openapi/quote/v1/depth', dict(symbol=symbol, limit=100)
		response = await self.http_requests(SpotBroker.GET, path, params=params)
		return response
	
	async def kline(self, symbol, interval):
		path, params = '/openapi/quote/v1/klines', dict(
			symbol=symbol, interval=self.intervals[SpotBroker.CandleIntervals.index(interval)], limit=500
		)
		response = await self.http_requests(SpotBroker.GET, path, params=params)
		return response
	
	async def trade(self, symbol):
		path, params = '/openapi/quote/v1/depth', dict(symbol=symbol)
		response = await self.http_requests(SpotBroker.GET, path, params=params)
		return [dict(
			time=item['time'], side=SpotBroker.Buy if item['isBuyerMaker'] else SpotBroker.Sell,
			price=item['price'], quantity=item['qty']
		) for item in response]
	
	async def ticker(self, symbol):
		path, params = '/openapi/quote/v1/ticker/24hr', dict(symbol=symbol)
		response = await self.http_requests(SpotBroker.GET, path, params=params)
		return dict(
			last=response['lastPrice'], low24h=response['lowPrice'], open24h=response['openPrice'],
			high24h=response['highPrice'], volume24h=response['volume']
		)
	
	async def balance(self):
		path, params = '/openapi/v1/account', self.sign()
		response = await self.http_requests(SpotBroker.GET, path, params=params)
		return [dict(
			asset=item['asset'], frozen=item['locked'], available=item['free']
		) for item in response['balances']]
	
	async def order_detail(self, order_id, symbol):
		path, params = '/openapi/v1/order', self.sign(symbol=symbol, orderId=order_id)
		response = await self.http_requests(SpotBroker.GET, path, params=params)
		return self.order_format(symbol, response)
	
	async def create_order(self, symbol, mold, side, quantity, line=None, direction=None, price=None):
		path, params = '/openapi/v1/order', self.sign(
			symbol=symbol, side=side.upper(), type=mold.upper(), price=self.decimal_to_string(price),
			quantity=self.decimal_to_string(quantity)
		)
		response = await self.http_requests(SpotBroker.POST, path, params=params)
		return response.get('orderId')
	
	async def cancel_order(self, order_id, symbol):
		path, params = '/openapi/v1/order', self.sign(
			orderId=order_id, symbol=symbol
		)
		response = await self.http_requests(SpotBroker.DELETE, path, params=params)
		return response.get('status') == 'CANCELED'
	
	async def active_orders(self, symbol):
		path, params = '/openapi/v1/openOrders', self.sign(
			symbol=symbol, limit=1000
		)
		response = await self.http_requests(SpotBroker.GET, path, params=params)
		return [self.order_format(symbol, order) for order in response]
	
	async def history_orders(self, symbol, start=None, end=None):
		path, params = '/openapi/v1/historyOrders', self.sign(
			symbol=symbol, limit=1000
		)
		response = await self.http_requests(SpotBroker.GET, path, params=params)
		return [self.order_format(symbol, order) for order in response]
	
	async def history_trades(self, symbol, start=None, end=None):
		path, params = '/openapi/v1/myTrades', self.sign(
			symbol=symbol, limit=1000
		)
		response = await self.http_requests(SpotBroker.GET, path, params=params)
		return [self.trade_format(symbol, trade) for trade in response]
